#include "wx/wxprec.h"
#include "wx/apptrait.h"
#include "wx/cmdline.h"
#include "wx/evtloop.h"
#include "wx/msgout.h"
#include "wx/thread.h"
#include "wx/vidmode.h"
#include "wx/ptr_scpd.h"

#ifdef __WXDEBUG__
#include "wx/stackwalk.h"
#endif

#include "wx/fontmap.h"
#include "wx/build.h"

// wxCore

wxList wxPendingDelete;
wxDEFINE_TIED_SCOPED_PTR_TYPE( wxEventLoop )

wxAppBase::wxAppBase() {
  m_topWindow = ( wxWindow * )NULL;
  m_useBestVisual = false;
  m_forceTrueColour = false;
  m_isActive = true;
  m_mainLoop = NULL;
  m_exitOnFrameDelete = Later;
}

bool wxAppBase::Initialize( int& argcOrig, wxChar **argvOrig ) {
  if( !wxAppConsole::Initialize( argcOrig, argvOrig ) ) {
    return false;
  }
  #if wxUSE_THREADS
  wxPendingEventsLocker = new wxCriticalSection;
  #endif
  wxInitializeStockLists();
  wxBitmap::InitStandardHandlers();
  return true;
}

wxAppBase::~wxAppBase() {
}

void wxAppBase::CleanUp() {
  DeletePendingObjects();
  while( !wxTopLevelWindows.empty() ) {
    delete wxTopLevelWindows.GetFirst()->GetData();
  }
  wxBitmap::CleanUpHandlers();
  wxStockGDI::DeleteAll();
  wxDeleteStockLists();
  delete wxTheColourDatabase;
  wxTheColourDatabase = NULL;
  delete wxPendingEvents;
  wxPendingEvents = NULL;
  #if wxUSE_THREADS
  delete wxPendingEventsLocker;
  wxPendingEventsLocker = NULL;
  #if wxUSE_VALIDATORS
  ( ( wxEvtHandler& ) wxDefaultValidator ).ClearEventLocker();
  #endif
  #endif
}

wxWindow* wxAppBase::GetTopWindow() const {
  wxWindow* window = m_topWindow;
  if( window == NULL && wxTopLevelWindows.GetCount() > 0 ) {
    window = wxTopLevelWindows.GetFirst()->GetData();
  }
  return window;
}

wxVideoMode wxAppBase::GetDisplayMode() const {
  return wxVideoMode();
}

wxLayoutDirection wxAppBase::GetLayoutDirection() const {
  const wxLocale *const locale = wxGetLocale();
  if( locale ) {
    const wxLanguageInfo *const
    info = wxLocale::GetLanguageInfo( locale->GetLanguage() );
    if( info ) {
      return info->LayoutDirection;
    }
  }
  return wxLayout_Default;
}

#if wxUSE_CMDLINE_PARSER

#define OPTION_THEME   _T("theme")
#define OPTION_MODE    _T("mode")

void wxAppBase::OnInitCmdLine( wxCmdLineParser& parser ) {
  wxAppConsole::OnInitCmdLine( parser );
  static const wxCmdLineEntryDesc cmdLineGUIDesc[] = {
    #ifdef __WXUNIVERSAL__
    {
      wxCMD_LINE_OPTION,
      wxEmptyString,
      OPTION_THEME,
      gettext_noop( "specify the theme to use" ),
      wxCMD_LINE_VAL_STRING,
      0x0
    },
    #endif

    #if defined(__WXMGL__)
    {
      wxCMD_LINE_OPTION,
      wxEmptyString,
      OPTION_MODE,
      gettext_noop( "specify display mode to use (e.g. 640x480-16)" ),
      wxCMD_LINE_VAL_STRING,
      0x0
    },
    #endif

    {
      wxCMD_LINE_NONE,
      wxEmptyString,
      wxEmptyString,
      wxEmptyString,
      wxCMD_LINE_VAL_NONE,
      0x0
    }
  };
  parser.SetDesc( cmdLineGUIDesc );
}

bool wxAppBase::OnCmdLineParsed( wxCmdLineParser& parser ) {
  #ifdef __WXUNIVERSAL__
  wxString themeName;
  if( parser.Found( OPTION_THEME, &themeName ) ) {
    wxTheme *theme = wxTheme::Create( themeName );
    if( !theme ) {
      wxLogError( _( "Unsupported theme '%s'." ), themeName.c_str() );
      return false;
    }
    delete wxTheme::Get();
    wxTheme::Set( theme );
  }
  #endif
  #if defined(__WXMGL__)
  wxString modeDesc;
  if( parser.Found( OPTION_MODE, &modeDesc ) ) {
    unsigned w, h, bpp;
    if( wxSscanf( modeDesc.c_str(), _T( "%ux%u-%u" ), &w, &h, &bpp ) != 3 ) {
      wxLogError( _( "Invalid display mode specification '%s'." ), modeDesc.c_str() );
      return false;
    }
    if( !SetDisplayMode( wxVideoMode( w, h, bpp ) ) ) {
      return false;
    }
  }
  #endif
  return wxAppConsole::OnCmdLineParsed( parser );
}

#endif

int wxAppBase::MainLoop() {
  wxEventLoopTiedPtr mainLoop( &m_mainLoop, new wxEventLoop );
  return m_mainLoop->Run();
}

void wxAppBase::ExitMainLoop() {
  if( m_mainLoop && m_mainLoop->IsRunning() ) {
    m_mainLoop->Exit( 0 );
  }
}

bool wxAppBase::Pending() {
  wxEventLoop * const loop = wxEventLoop::GetActive();
  return loop && loop->Pending();
}

bool wxAppBase::Dispatch() {
  wxEventLoop * const loop = wxEventLoop::GetActive();
  return loop && loop->Dispatch();
}

bool wxAppBase::OnInitGui() {
  #ifdef __WXUNIVERSAL__
  if( !wxTheme::Get() && !wxTheme::CreateDefault() ) {
    return false;
  }
  #endif
  return true;
}

int wxAppBase::OnRun() {
  if( m_exitOnFrameDelete == Later ) {
    m_exitOnFrameDelete = Yes;
  }
  return MainLoop();
}

int wxAppBase::OnExit() {
  #ifdef __WXUNIVERSAL__
  delete wxTheme::Set( NULL );
  #endif
  return wxAppConsole::OnExit();
}

void wxAppBase::Exit() {
  ExitMainLoop();
}

wxAppTraits *wxAppBase::CreateTraits() {
  return new wxGUIAppTraits;
}

void wxAppBase::SetActive( bool active, wxWindow * ( lastFocus ) ) {
  if( active == m_isActive ) {
    return;
  }
  m_isActive = active;
  wxActivateEvent event( wxEVT_ACTIVATE_APP, active );
  event.SetEventObject( this );
  ( void )ProcessEvent( event );
}

void wxAppBase::DeletePendingObjects() {
  wxList::compatibility_iterator node = wxPendingDelete.GetFirst();
  while( node ) {
    wxObject *obj = node->GetData();
    if( wxPendingDelete.Member( obj ) ) {
      wxPendingDelete.Erase( node );
    }
    delete obj;
    node = wxPendingDelete.GetFirst();
  }
}

bool wxAppBase::ProcessIdle() {
  ProcessPendingEvents();
  wxIdleEvent event;
  bool needMore = false;
  wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
  while( node ) {
    wxWindow* win = node->GetData();
    if( SendIdleEvents( win, event ) ) {
      needMore = true;
    }
    node = node->GetNext();
  }
  event.SetEventObject( this );
  ( void ) ProcessEvent( event );
  if( event.MoreRequested() ) {
    needMore = true;
  }
  DeletePendingObjects();
  #if wxUSE_LOG
  wxLog::FlushActive();
  #endif
  wxUpdateUIEvent::ResetUpdateTime();
  return needMore;
}

bool wxAppBase::SendIdleEvents( wxWindow* win, wxIdleEvent& event ) {
  bool needMore = false;
  win->OnInternalIdle();
  if( wxIdleEvent::CanSend( win ) ) {
    event.SetEventObject( win );
    win->GetEventHandler()->ProcessEvent( event );
    if( event.MoreRequested() ) {
      needMore = true;
    }
  }
  wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
  while( node ) {
    wxWindow *child = node->GetData();
    if( SendIdleEvents( child, event ) ) {
      needMore = true;
    }
    node = node->GetNext();
  }
  return needMore;
}

void wxAppBase::OnIdle( wxIdleEvent & ( event ) ) {
}

#if wxUSE_EXCEPTIONS

bool wxAppBase::OnExceptionInMainLoop() {
  throw;
  #if defined(__DMC__) || (defined(_MSC_VER) && _MSC_VER < 1200)
  return false;
  #endif
}

#endif

#if wxUSE_LOG

wxLog *wxGUIAppTraitsBase::CreateLogTarget() {
  #if wxUSE_LOGGUI
  return new wxLogGui;
  #else
  return new wxLogStderr;
  #endif
}

#endif

wxMessageOutput *wxGUIAppTraitsBase::CreateMessageOutput() {
  return new wxMessageOutputStderr;
}

wxFontMapper *wxGUIAppTraitsBase::CreateFontMapper() {
  return new wxFontMapper;
}


wxRendererNative *wxGUIAppTraitsBase::CreateRenderer() {
  return NULL;
}

#ifdef __WXDEBUG__

bool wxGUIAppTraitsBase::ShowAssertDialog( const wxString& msg ) {
  #if defined(__WXMSW__) || !wxUSE_MSGDLG
  return wxAppTraitsBase::ShowAssertDialog( msg );
  #else
  wxString msgDlg = msg;
  wxFprintf( stderr, wxT( "[Debug] Generating a stack trace... please wait" ) );
  fflush( stderr );
  const wxString stackTrace = GetAssertStackTrace();
  if( !stackTrace.empty() ) {
    msgDlg << _T( "\n\nCall stack:\n" ) << stackTrace;
  }
  msgDlg += wxT( "\nDo you want to stop the program?\n" )
            wxT( "You can also choose [Cancel] to suppress " )
            wxT( "further warnings." );
  switch( wxMessageBox( msgDlg, wxT( "wxWidgets Debug Alert" ),wxYES_NO | wxCANCEL | wxICON_STOP ) ) {
    case wxYES:
      wxTrap();
      break;
    case wxCANCEL:
      return true;
  }
  return false;
  #endif
}

#endif

void wxGUIAppTraitsBase::ScheduleForDestroy( wxObject *object ) {
  if( !wxPendingDelete.Member( object ) ) {
    wxPendingDelete.Append( object );
  }
}

void wxGUIAppTraitsBase::RemoveFromPendingDelete( wxObject *object ) {
  wxPendingDelete.DeleteObject( object );
}

#include "wx/unix/gsockunx.h"

GSocketGUIFunctionsTable* wxGUIAppTraitsBase::GetSocketGUIFunctionsTable() {
  static GSocketGUIFunctionsTableConcrete table;
  return &table;
}
